/**
 * @file fileTransferCmds.c
 * @author MTF-1
 *
 * @date 26-01-2011 DD-MM-YYYY
 * @version 0.8
 *
 * @section DESCRIPTION
 *
 * Module for file transfer commands.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "connectionCmds.h"
#include "fileTransferCmds.h"
#include "tools.h"

#define ERROR 1
#define NOERROR 0
#define TRUE 1
#define FALSE 0
#define UP_RCVBUFLEN 10
#define UP_SNDBUFLEN 1460
#define UP_CMDSNDBUFLEN 155
#define DOWN_RCVBUFLEN 1460
#define DOWN_CMDSNDBUFLEN 155
#define DOWN_FILEOVWRTBUF 10

typedef unsigned char bool;

/**
 * Upload a specified file on the client under a specified name to the server.
 *
 * @param ptr_sFilenameClient name of the file located on the client (source).
 * @param ptr_sFilenameServer name of the file located on the server (destination).
 * @return 1 on error (failed to upload) and 0 on no error.
 * @author Rik Janssen, Robbert Busser
 */
int cmdUpload(char *ptr_sFilenameClient, char *ptr_sFilenameServer)
{
    char sRecvBuf[UP_RCVBUFLEN];
    char sCmdSndBuf[UP_CMDSNDBUFLEN] = "UPLOAD ";
    char sSendBuf[UP_SNDBUFLEN];
    int i, iInFileFD = 0, iBytesRcvd = 0, iBytesRead = 0, iBytesSend = 0;

    /*Open file*/
    printf("Opening file %s...\n", ptr_sFilenameClient);
    if((iInFileFD = open(ptr_sFilenameClient, O_RDONLY)) == -1)
    {
        perror("Error: can't open file.\n");
        return ERROR;
    }

    printf("File opened. Starting upload...\n");

    if(chkConnection())
    {
        fprintf(stderr, "Error: there's currently no active connection.\n");
        close(iInFileFD);
        return ERROR;
    }

    /*Empty receive buffer*/
    for(i=0; i<UP_RCVBUFLEN; i++)
    {
        sRecvBuf[i] = 0;
    }

    /*Send UPLOAD command*/
    strcat(sCmdSndBuf, ptr_sFilenameServer);
    if(send(iSocketFD, &sCmdSndBuf, strlen(sCmdSndBuf)+1, 0) == -1)
    {
        perror("Error: failed to send \"UPLOAD\" command.\n");
        close(iInFileFD);
        return ERROR;
    }

    /*Receive return message from server*/
    if((iBytesRcvd = recv(iSocketFD, &sRecvBuf, UP_RCVBUFLEN-1, 0)) <= 0)
    {
        perror("Error: failed to receive upload confirmation from the server.\n");
        close(iInFileFD);
        return ERROR;
    }

    sRecvBuf[iBytesRcvd] = '\0'; /*Terminate the received message*/

    /*Check the received message*/
    if(!strcasecmp(sRecvBuf, "READY"))
    {
        printf("Uploading %s under %s to %s...\n", ptr_sFilenameClient, ptr_sFilenameServer, sServerName);

        /*Begin reading file*/
        while((iBytesRead = read(iInFileFD, &sSendBuf, UP_SNDBUFLEN)) > 0)
        {
            /*Send file to server*/
            if((iBytesSend = send(iSocketFD, &sSendBuf, iBytesRead, 0)) == -1)
            {
                perror("Error: failed to send data to server.\n");
                close(iInFileFD);
                return ERROR;
            }
        }

        /*Reading of file complete -> close file and check if read completed corectly*/
        close(iInFileFD);
        if(iBytesRead == -1)
        {
            fprintf(stderr, "Error: read error on file during data transfer.\n");
            return ERROR;
        }

        printf("Done!\n");
    }
    if(!strcasecmp(sRecvBuf, "ERROR"))
    {
        fprintf(stderr, "Error: file already exists.\n");
        close(iInFileFD);
        return ERROR;
    }

    return NOERROR; /*The file has been uploaded succesfully*/
}

/**
 * Download a specified file from the server under a specified name to the client.
 *
 * @param ptr_sFilenameServer name of the file located on the server (source).
 * @param ptr_sFilenameClient name of the file located on the client (destination).
 * @return 1 on error (failed to download) and 0 on no error.
 * @author Rik Janssen
 */
int cmdDownload(char *ptr_sFilenameServer, char *ptr_sFilenameClient)
{
    char sRecvBuf[DOWN_RCVBUFLEN];
    char sCmdSndBuf[DOWN_CMDSNDBUFLEN] = "DOWNLOAD ";
    char sOverwriteBuf[DOWN_FILEOVWRTBUF];
    int i, iOutFileFD = 0, iBytesRcvd = 0, iBytesWrite = 0;
    bool bFileExisted = TRUE;

    /*Open or create file*/
    printf("Creating file %s...\n", ptr_sFilenameClient);
    if((iOutFileFD = open(ptr_sFilenameClient, O_WRONLY)) == -1)
    {
        /*Did the file not exist?*/
        if(ENOENT)
        {
            bFileExisted = FALSE;
            if((iOutFileFD = creat(ptr_sFilenameClient, 0600)) == -1)
            {
                perror("Error: failed to create file.\n");
                return ERROR;
            }
        }
        else
        {
            perror("Error: failed to open existing file.\n");
            return ERROR;
        }
    }
    
    /*Handle existing file*/
    if(bFileExisted)
    {
        printf("Warning: file already exists. Overwrite? [yes/no]\n");
        getCommand(sOverwriteBuf, DOWN_FILEOVWRTBUF);
        if(strcasecmp(sOverwriteBuf, "yes"))
        {
            close(iOutFileFD);
            return ERROR; /*Overwrite was no*/
        }

        /*Empty existing file*/
        close(iOutFileFD);
        if((iOutFileFD = open(ptr_sFilenameClient, O_WRONLY|O_TRUNC)) == -1)
        {
            perror("Error: failed to ready file for writing (file may now be corrupted).\n");
            return ERROR;
        }
    }

    printf("Starting download...\n");

    if(chkConnection())
    {
        fprintf(stderr, "Error: there's currently no active connection.\n");
        close(iOutFileFD);
        if(!bFileExisted)
        {
            unlink(ptr_sFilenameClient);
        }
        return ERROR;
    }

    /*Empty receive buffer*/
    for(i=0; i<DOWN_RCVBUFLEN; i++)
    {
        sRecvBuf[i] = 0;
    }

    /*Send download command*/
    strcat(sCmdSndBuf, ptr_sFilenameServer);
    if(send(iSocketFD, &sCmdSndBuf, strlen(sCmdSndBuf)+1, 0) == -1)
    {
        perror("Error: failed to send \"DOWNLOAD\" command.\n");
        close(iOutFileFD);
        if(!bFileExisted)
        {
            unlink(ptr_sFilenameClient);
        }
        return ERROR;
    }

    /*Receive return message from server*/
    if((iBytesRcvd = recv(iSocketFD, &sRecvBuf, DOWN_RCVBUFLEN-1, 0)) <= 0)
    {
        perror("Error: failed to receive download confirmation from the server.\n");
        close(iOutFileFD);
        if(!bFileExisted)
        {
            unlink(ptr_sFilenameClient);
        }
        return ERROR;
    }

    sRecvBuf[iBytesRcvd] = '\0'; /*Terminate the received message*/

    /*Check the received message*/
    if(!strcasecmp(sRecvBuf, "READY"))
    {
        if(send(iSocketFD, "READY", 6, 0) == -1)
        {
            perror("Error: failed to send \"READY\" command.\n");
            close(iOutFileFD);
            if(!bFileExisted)
            {
                unlink(ptr_sFilenameClient);
            }
            return ERROR;
        }

        printf("Downloading %s from %s to %s...\n", ptr_sFilenameServer, sServerName, ptr_sFilenameClient);
        
        /*Begin receiving and writing data*/
        do
        {
            /*Receive data*/
            if((iBytesRcvd = recv(iSocketFD, &sRecvBuf, DOWN_RCVBUFLEN, 0)) == -1)
            {
                perror("Error: not all data has been received. Output file may be corrupted.\n");
                close(iOutFileFD);
                if(!bFileExisted)
                {
                    unlink(ptr_sFilenameClient);
                }
                return ERROR;
            }
            /*Write data*/
            if((iBytesWrite = write(iOutFileFD, &sRecvBuf, iBytesRcvd)) == -1)
            {
                perror("Error: failed to write received data to file.\n");
                close(iOutFileFD);
                if(!bFileExisted)
                {
                    unlink(ptr_sFilenameClient);
                }
                else
                {
                    printf("Warning: %s may contain corrupted or incomplete data.\n", ptr_sFilenameClient);
                }
                return ERROR;
            }
        }
        while(iBytesRcvd == DOWN_RCVBUFLEN);

        /*Receiving of file complete -> close file*/
        close(iOutFileFD);
        printf("Done!\n");
    }
    if(!strcasecmp(sRecvBuf, "ERROR"))
    {
        printf("Error: file %s does not exists on %s.\n", ptr_sFilenameServer, sServerName);
        close(iOutFileFD);
        if(!bFileExisted)
        {
            unlink(ptr_sFilenameClient);
        }
        return ERROR;
    }

    return NOERROR; /*The file has been downloaded succesfully*/
}
